package top.heathylife.pagehelperx.converter;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import top.heathylife.pagehelperx.other.BusinessException;
import top.heathylife.pagehelperx.other.Get;
import top.heathylife.pagehelperx.other.TypeFunction;
import top.heathylife.pagehelperx.wrappers.FunctionWrapper;

import java.math.BigDecimal;
import java.util.function.Predicate;

/**
 * between and 比较器,实现 数字,日期(yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss).比较
 *
 * @author maoxiaomeng
 */
@Slf4j
public class BetweenConverter<T> implements PredicateConverter<T> {

    @Override
    public Predicate<T> toPredicate(FunctionWrapper<T> functionWrapper) {
        return e -> {
            TypeFunction<T, ?> key = functionWrapper.getKey();
            Object[] value = functionWrapper.getValue();
            String name = Get.name(key);
            log.debug("过滤条件:[between and]:key:[{}],value:[{},{}]", name, value[0], value[1]);
            JSON parse = JSONUtil.parse(e);
            Object byPath = parse.getByPath(name);
            Object o1 = value[0];
            Object o2 = value[1];
            //分为日期和,数字类型来比较
            String str = StrUtil.toString(byPath);
            String o1Str = StrUtil.toString(o1);
            String o2Str = StrUtil.toString(o2);
            if (NumberUtil.isNumber(str)) {
                if (!NumberUtil.isNumber(o1Str) || !NumberUtil.isNumber(o2Str)) {
                    throw new BusinessException(StrUtil.format("Between 操作符,比较值类型为({}),参数值类型({},{})类型不一致", "number", o1, o2));
                }
                return isGreaterThan((Number) byPath, (Number) o1) && isLessThan((Number) byPath, (Number) o2);
            }
            try {
                //如果是日期类型
                //2000-01-01
                if (str.length() == 10) {
                    DateTime dateTime = DateUtil.parseDate(str);
                    if (!isValidDate(o1Str) || !isValidDate(o2Str)) {
                        throw new BusinessException(StrUtil.format("Between 操作符,比较值类型为({}),参数值类型({},{})类型不一致", "date", o1, o2));
                    }
                    return dateTime.isAfter(DateUtil.parseDate(o1Str)) && dateTime.isBefore(DateUtil.parseDate(o2Str));
                }
                //2000-01-01 00:00:00
                //2000-01-01T00:00:00
                if (str.length() == 19) {
                    if (str.contains("T")) {
                        str = str.replace("T", " ");
                    }
                    DateTime dateTime = DateUtil.parseDateTime(str);
                    if (isValidDateTime(o1Str) && isValidDateTime(o2Str)) {
                        return dateTime.isAfter(DateUtil.parseDateTime(o1Str)) && dateTime.isBefore(DateUtil.parseDateTime(o2Str));
                    }
                    if (isValidDate(o1Str) && isValidDate(o2Str)) {
                        return dateTime.isAfter(DateUtil.parseDate(o1Str)) && dateTime.isBefore(DateUtil.parseDate(o2Str));
                    }
                    throw new BusinessException(StrUtil.format("Between 操作符,比较值类型为({}),参数值类型({},{})类型不一致", "datetime", o1, o2));
                }
            } catch (Exception ex) {
                //ignore
            }
            return false;
        };
    }

    public static boolean isValidDate(String str) {
        try {
            //2000-01-01
            if (str.length() == 10) {
                DateUtil.parseDate(str);
                return true;
            }
        } catch (Exception ex) {
            //ignore
            return false;
        }
        return false;
    }

    public static boolean isValidDateTime(String str) {
        try {
            //2000-01-01 00:00:00
            if (str.length() == 19) {
                if (str.contains("T")) {
                    str = str.replace("T", " ");
                }
                DateUtil.parseDateTime(str);
                return true;
            }
        } catch (Exception ex) {
            //ignore
            return false;
        }
        return false;
    }

    public static boolean isGreaterThan(Number n1, Number n2) {
        return NumberUtil.isGreaterOrEqual(new BigDecimal(n1.toString()), new BigDecimal(n2.toString()));
    }

    public static boolean isLessThan(Number n1, Number n2) {
        return NumberUtil.isLessOrEqual(new BigDecimal(n1.toString()), new BigDecimal(n2.toString()));
    }
}
